﻿/*Example of a CAPL DLL implementation */
/*这个文件是一个测试所帅哥做的模板，我们在实际编程的时候，
  只需要写好自己的函数，并添加进文件最后的函数列表中，
  就能再capl中调用了*/

#include "capldll.h"

// ============================================================================
// 全局变量
// ============================================================================

static unsigned long data = 0;
static char dlldata[100];

char        gModuleName[_MAX_FNAME];  // 这个dll的文件名 
HINSTANCE   gModuleHandle;            // 该DLL的窗口实例句柄
VCaplMap    gCaplMap;   
VServiceMap gServiceMap;


// ============================================================================
// CaplInstanceData
// 
// 单个CAPL块的本地数据。 
//
// 一个CAPL-DLL可以被一个以上的CAPL-Block使用，所以每一个
// 信息就像CAPL中的globale变量，现在必须被包装成
// 一个对象的实例。
// ============================================================================
class CaplInstanceData
{
public:
  CaplInstanceData(VIACapl* capl);
  
  void GetCallbackFunctions();
  void ReleaseCallbackFunctions();

  // 类函数的定义。 
  // 这个类函数将调用
  // CAPL回调函数
  uint32 ShowValue(uint32 x);
  uint32 ShowDates(int16 x, uint32 y, int16 z);
  void   DllInfo(char* x);
  void   ArrayValues(uint32 flags, uint32 numberOfDatabytes, uint8 databytes[], uint8 controlcode);
  void DllVersion(char* y);

private:  

  // CAPL回调函数的指针
  VIACaplFunction*  mShowValue;
  VIACaplFunction*  mShowDates;
  VIACaplFunction*  mDllInfo;
  VIACaplFunction*  mArrayValues;
  VIACaplFunction*  mDllVersion;

  VIACapl*          mCapl;
};


CaplInstanceData::CaplInstanceData(VIACapl* capl) 
  // 这个函数将用NLL指针初始化CAPL回调函数
 : mCapl(capl), 
   mShowValue(NULL),
   mShowDates(NULL),
   mDllInfo(NULL),
   mArrayValues(NULL),
   mDllVersion(NULL)
{}

static bool sCheckParams(VIACaplFunction* f, char rtype, char* ptype)
{
  char      type;
  int32     pcount;
  VIAResult rc;

  // 检查返回类型
  rc = f->ResultType(&type);
  if (rc!=kVIA_OK || type!=rtype)
  {
    return false;
  }

  // 检查参数的数量
  rc = f->ParamCount(&pcount);
  if (rc!=kVIA_OK || strlen(ptype)!=pcount )
  {
    return false;
  }

  // 检查参数的类型
  for (int i=0; i<pcount; ++i)
  {
    rc = f->ParamType(&type, i);
    if (rc!=kVIA_OK || type!=ptype[i])
    {
      return false;
    }
  }

  return true;
}

static VIACaplFunction* sGetCaplFunc(VIACapl* capl, const char * fname, char rtype, char* ptype)
{
  VIACaplFunction* f; 

  // 获取capl函数对象
  VIAResult rc =  capl->GetCaplFunction(&f, fname);
  if (rc!=kVIA_OK || f==NULL) 
  {
    return NULL;
  }

  // 检查函数的签名
  if ( sCheckParams(f, rtype, ptype) )
  {
     return f;
  }
  else
  {
    capl->ReleaseCaplFunction(f);
    return NULL;
  }
}

void CaplInstanceData::GetCallbackFunctions()
{
  //获取一个CAPL函数句柄。该句柄在测量结束或调用ReleaseCaplFunction之前一直有效。
  mShowValue   = sGetCaplFunc(mCapl, "CALLBACK_ShowValue", 'D', "D");
  mShowDates   = sGetCaplFunc(mCapl, "CALLBACK_ShowDates", 'D', "IDI");
  mDllInfo     = sGetCaplFunc(mCapl, "CALLBACK_DllInfo", 'V', "C");
  mArrayValues = sGetCaplFunc(mCapl, "CALLBACK_ArrayValues", 'V', "DBB");
  mDllVersion  = sGetCaplFunc(mCapl, "CALLBACK_DllVersion", 'V', "C");
}

void CaplInstanceData::ReleaseCallbackFunctions()
{
  // 释放所有请求的回调函数 
  mCapl->ReleaseCaplFunction(mShowValue);
  mShowValue = NULL;
  mCapl->ReleaseCaplFunction(mShowDates);
  mShowDates = NULL;
  mCapl->ReleaseCaplFunction(mDllInfo);
  mDllInfo = NULL;
  mCapl->ReleaseCaplFunction(mArrayValues);
  mArrayValues = NULL;
  mCapl->ReleaseCaplFunction(mDllVersion);
  mDllVersion = NULL;
}

void CaplInstanceData::DllVersion(char* y)
{
  // 为CAPL的调用堆栈准备好参数。 
  // 数组在堆栈中使用8个字节，4个字节为元素数，
  // 4个字节为数组的指针
  int32 sizeX = strlen(y)+1;

  uint8 params[8];               // 调用堆栈的参数，共8字节  
  memcpy(params+0, &sizeX, 4);   // 第一个参数的数组大小，4字节
  memcpy(params+4, &y,     4);   // 第一个参数的数组指针，4字节
  
  if(mDllVersion!=NULL)
  {
    uint32 result; // dummy variable
    VIAResult rc =  mDllVersion->Call(&result, params);
  }
}


uint32 CaplInstanceData::ShowValue(uint32 x)
{
  void* params = &x;   // 调用堆栈的参数

  uint32 result;

  if(mShowValue!=NULL)
  {
    VIAResult rc =  mShowValue->Call(&result, params);
    if (rc==kVIA_OK)
    {
       return result;
    }
  }    
  return -1;
}

uint32 CaplInstanceData::ShowDates(int16 x, uint32 y, int16 z)
{
  // 为CAPL的调用栈准备好参数。堆栈从上往下增长，
  // 所以参数列表中的第一个参数是内存中最后一个参数。
  // CAPL对参数也使用32位对齐。
  uint8 params[12];         // 呼叫堆栈的参数，共12字节   
  memcpy(params+0, &z, 2);  // 第三参数，偏移量0，2字节
  memcpy(params+4, &y, 4);  // 第二个参数，偏移量4，4字节
  memcpy(params+8, &x, 2);  // 第一个参数，偏移量8，2字节

  uint32 result;

  if(mShowDates!=NULL)
  {
    VIAResult rc =  mShowDates->Call(&result, params);
    if (rc==kVIA_OK)
    {
       return rc;   // call successful
    }
  } 
    
  return -1; // call failed
}

void CaplInstanceData::DllInfo(char* x)
{
  // 为CAPL的调用堆栈准备好参数。 
  // 数组在堆栈中使用8个字节，4个字节为元素数，
  // 4个字节为数组的指针
  int32 sizeX = strlen(x)+1;

  uint8 params[8];               // 呼叫堆栈的参数，共8字节    
  memcpy(params+0, &sizeX, 4);   // 第一个参数的数组大小，4字节
  memcpy(params+4, &x,     4);   // 第一个参数的数组指针，4字节
  
  if(mDllInfo!=NULL)
  {
    uint32 result; // dummy variable
    VIAResult rc =  mDllInfo->Call(&result, params);
  }
}

void CaplInstanceData::ArrayValues(uint32 flags, uint32 numberOfDatabytes, uint8 databytes[], uint8 controlcode)
{
  // 为CAPL的调用栈准备好参数。栈的增长
  // 从上到下，所以参数列表中的第一个参数是最后一个
  // 一个在内存中。CAPL对参数也使用32位对齐。
  // 数组在堆栈中使用8个字节，4个字节为元素的数量、
  // 和4个字节的指向数组的指针
  
  uint8 params[16];                           // 呼叫堆栈的参数，共16字节  
  memcpy(params+ 0, &controlcode,       1);   // 第三参数，偏移量0，1字节
  memcpy(params+ 4, &numberOfDatabytes, 4);   // 第二个参数（阵列大小），偏移量4，4字节
  memcpy(params+ 8, &databytes,         4);   // 第二个参数（阵列指针），偏移量8，4字节
  memcpy(params+12, &flags,             4);   // 第一个参数，偏移量12，4字节

  if(mArrayValues!=NULL)
  {
    uint32 result; // 虚拟变量
    VIAResult rc =  mArrayValues ->Call(&result, params);
  }

}

CaplInstanceData* GetCaplInstanceData(uint32 handle)
{
  VCaplMap::iterator lSearchResult(gCaplMap.find(handle));
  if ( gCaplMap.end()==lSearchResult )
  {
    return NULL;
  } else {
    return lSearchResult->second;
  }
}

// ============================================================================
// CaplInstanceData
// 
// 单个CAPL块的本地数据。
//
// 一个CAPL-DLL可以被一个以上的CAPL-Block使用，所以每一块信息，
// 就像CAPL中的全局变量一样，现在必须被包装成一个对象的实例。
// ============================================================================

void CAPLEXPORT far CAPLPASCAL appInit (uint32 handle)
{
  CaplInstanceData* instance = GetCaplInstanceData(handle);
  if ( NULL==instance )
  {
    VServiceMap::iterator lSearchService(gServiceMap.find(handle));
    if ( gServiceMap.end()!=lSearchService )
    {
      VIACapl* service = lSearchService->second;
      try 
      {
        instance = new CaplInstanceData(service);
      }
      catch ( std::bad_alloc& )
      {
        return; // proceed without change
      }
      instance->GetCallbackFunctions();
      gCaplMap[handle] = instance;
    }
  }
}    

void CAPLEXPORT far CAPLPASCAL appEnd (uint32 handle)
{
  CaplInstanceData* inst = GetCaplInstanceData(handle);
  if (inst==NULL)
  {
    return;
  }
  inst->ReleaseCallbackFunctions();

  delete inst;
  inst = NULL; 
  gCaplMap.erase(handle);
}

// ============================================================================
// VIARegisterCDLL
// ============================================================================

VIACLIENT(void) VIARegisterCDLL (VIACapl* service)
{
  uint32    handle;
  VIAResult result;

  if (service==NULL)
  {
    return;
  }

  result = service->GetCaplHandle(&handle);
  if(result!=kVIA_OK)
  {
    return;
  }

  // appInit (internal) resp. "DllInit" (CAPL code) has to follow
  gServiceMap[handle] = service;
}

void ClearAll()
{
  // 销毁由该DLL创建的对象 
  // 可能会因被遗忘的DllEnd调用而导致。
  VCaplMap::iterator lIter=gCaplMap.begin();
  const long cNumberOfEntries = gCaplMap.size();
  long i = 0;
  while ( lIter!=gCaplMap.end() && i<cNumberOfEntries )
  {
    appEnd( (*lIter).first );
    lIter = gCaplMap.begin(); // 第一个元素应该已经消失了
    i++; // assure that no more erase trials take place than the original size of the map
  }

  // 只是为了清楚起见（会自动完成）。
  gCaplMap.clear();
  gServiceMap.clear();
}


// ============================================================================
// DllMain, DLL的入口点
// ============================================================================

BOOL WINAPI DllMain(HINSTANCE handle, DWORD reason, void*)
{     
  switch (reason) 
  {
    case DLL_PROCESS_ATTACH:
    {
      gModuleHandle = handle;
         
      // 获取模块的完整文件名
      char path_buffer[_MAX_PATH];
      DWORD result = GetModuleFileName(gModuleHandle, path_buffer,_MAX_PATH);

      // 将文件名分成若干部分
      char drive[_MAX_DRIVE];
      char dir[_MAX_DIR];
      char fname[_MAX_FNAME];
      char ext[_MAX_EXT];
#if _MSC_VER>=1400 // >= Visual Studio 2005
      _splitpath_s( path_buffer, drive, dir, fname, ext );
      strcpy_s(gModuleName, fname);
#else
      _splitpath( path_buffer, drive, dir, fname, ext );
      strcpy(gModuleName, fname);
#endif
      
      return 1;   // 表明DLL被成功初始化。
    }

    case DLL_PROCESS_DETACH:                                              
    {
      ClearAll();
      return 1;   // 表示DLL被成功释放。
    }
  }
  return 1;
}
